import json
import arrow
from urllib import parse
import traceback
from bson import ObjectId
from flask import jsonify, g, request, current_app, redirect
from flask_jwt_extended import create_access_token

from app.libs.redprint import RedPrint
from app.auth import permission_required
from app.db.models.packages import Packages
from app.db.models.balances import Balances
from app.db.models.charges import Charges
from app.db.models.jobs import Job
from app.db.models.users import User
from app.libs.enums import LimitType
from app.handler.v2_users import (
    LoginHelper,
    UserHelper,
    add_balance_check,
    recharge_used,
    give_plan,
    give_addition_package,
    calculate_feature,
    calculate_freq,
    agree_user_protocol
)
from app.libs.enums import ChargeType
from app.libs.utility import get_request_args
from app.libs.cas_api import request_cas
from app.errors import Success, ServerError, ParameterError
from app.log.logs import CUserLog, UUserLog, LoginLog, LogoutLog
from app.config.settings import CAS_CONFIG, JWT_ACCESS_TOKEN_EXPIRES, JWT_ACCESS_COOKIE_NAME
from app.validators.users import AgreementForm

api = RedPrint('users')


@api.route("/login", methods=["GET"])
def login():
    ticket = request.args.get("ticket")
    url_root = request.url_root
    service = request.args.get('service', '') or parse.urljoin(url_root, "api/v2/users/login")
    cas_login = parse.urljoin(CAS_CONFIG["base_url"], f'cas/login?service={service}')
    if not ticket:
        return redirect(cas_login)
    username, user_info = LoginHelper.validate_ticket(ticket, CAS_CONFIG.get('service', service))
    if not username:
        return ParameterError(msg="登录信息错误", code=401)
    user = LoginHelper.check_easm_is_exists(username)
    if not user:
        user = LoginHelper.create_user(username)
    LoginHelper.update_user_info(user, user_info)
    token = create_access_token(identity=user.id, additional_claims={"username": user.username, "entityType": "user"})
    log_data = {
        "message": "用户登录",
        "operator": user.username,
        "logSource": "front",
        "log_type": "auth",
        "detail": f"{user.username}登录",
        "uid": str(user.id),
        "actionMessage": "用户登录"
    }
    LoginLog(**log_data).save()
    return jsonify({"status": "success", "code": 200, "data": {"token": token, "username": user.username}})


@api.route("/logout", methods=["GET"])
def logout():
    user = g.user
    token = request.cookies.get(JWT_ACCESS_COOKIE_NAME, "")
    current_app.extensions["redis"].set(f"scanv_black_token_{token}", 1,
                                        ex=int(JWT_ACCESS_TOKEN_EXPIRES.total_seconds()))
    log_data = {
        "message": "用户注销",
        "operator": user.username,
        "logSource": "front",
        "log_type": "auth",
        "detail": "成功",
        "uid": str(user.id),
        "actionMessage": "注销"
    }
    LogoutLog(**log_data).save()
    url_root = request.url_root
    service = request.args.get('service', '') or parse.urljoin(url_root, "api/v2/users/login")
    cas_login = parse.urljoin(CAS_CONFIG["base_url"], f'cas/login?service={service}')
    cas_logout = parse.urljoin(CAS_CONFIG["base_url"], f'cas/logout?url={cas_login}')
    return redirect(cas_logout)


@api.route('/user_info', methods=['GET'])
def get_user_info():
    data = {
        'uid': g.user.id,
        "identified": g.user.identified,
        "sso": CAS_CONFIG.get("base_url", ""),
        "disclaimer_agree_time": g.user.disclaimerAgreeTime or "",
        "username": g.user.username,
        "enable_mfa": g.user.enableMfa,
    }
    return jsonify({'code': 200, 'msg': 'ok', 'results': data})


@api.route('/balance/get', methods=['GET'])
def get_balance():
    allow_keys = ('plan_name', 'username')

    if g.role.name == '普通用户':
        request_args = {'uid':  g.user.id}
    else:
        allow_args = ('uid', 'username')
        request_args = get_request_args(allow_args)

    user = UserHelper(request_args, use_plan=True, only_one=True).get_data()
    if user:
        results = {
            'id': str(user.get('_id')),
            'balance': user.get('balances').balance,
            'feature': user.get('balances').feature
        }
        for k in allow_keys:
            results[k] = user.get(k, '')
        defense_jobs = Job.objects.find({"$and": [{"defense_target_id": {"$exists": True}},
                                                  {"defense_target_id": {"$ne": ""}},
                                                  {"uid": user.get('_id')}]}).count()
        scanv_jobs = Job.objects.find({"defense_target_id": {"$exists": False}, "uid": user.get('_id')}).count()
        plan_name = results.get("plan_name", "")
        plan = Packages.objects.find({"name": plan_name}).first()
        plan_key = plan.key if plan else "scanv_free"
        if defense_jobs > 0 and scanv_jobs == 0 and plan_key == "scanv_free":
            results['plan_name'] = "源站安全检测"
    else:
        results = {}

    return jsonify({'code': 200, 'msg': 'ok', 'results': results})


@api.route('/balances/add/<string:uid>', methods=['POST'])
@permission_required('admin')
def add_balance(uid):
    """套餐购买"""
    request_args = json.loads(request.data)
    check = int(request_args.get('check', 0))
    items = request_args.get('items', [])
    operator = request_args.get('operator')

    recharge_used(uid)
    plan_item = dict()
    if check:
        pack_plan = None
        error_list = []
        if not (balance := Balances.objects.filter(uid=uid).first()):
            return jsonify(statusCode=500, msg=["用户balances不存在"])

        for item in items:
            if item.get('chargeType') == ChargeType.plan.value:
                plan_item = item
                if pack_plan:
                    return jsonify(statusCode=500, msg=["不能同时充两次套餐"])
                pack_plan = Packages.objects.filter(pk=item.get('chargeId'), chargeType=ChargeType.plan.value).first()

        for item in items:
            try:
                add_balance_check(item, uid, error_list, balance, pack_plan, plan_item)
                arrow.get(item.get('expireTime'))
            except Exception as e:
                traceback.print_exc()
                return jsonify(statusCode=500, msg=[getattr(e, 'msg', str(e))])

        if error_list:
            return jsonify(statusCode=500, msg=error_list)

        return jsonify(statusCode=200)

    i = 0
    pack_id_list = []
    for item in items:
        try:
            pack_id = item.get('chargeId')
            charge_num = int(item.get('number', 1))
            expire_time = arrow.get(item.get('expireTime')).datetime
            pack = Packages.objects.filter(pk=pack_id).first()
            pack_id_list.append(pack_id)
            if pack.chargeType == ChargeType.plan.value:
                give_plan(uid, pack, charge_num, expire_time)
                i += 1
            elif pack.chargeType == ChargeType.addition_package.value:
                give_addition_package(uid, pack, charge_num, expire_time)
                i += 1
        except Exception as e:
            current_app.logger.exception(e)

    o_uid = ObjectId(uid)
    update_dict = calculate_freq(o_uid)
    update_dict['feature'] = calculate_feature(o_uid)

    Balances.objects(uid=uid).update(**update_dict)

    UUserLog().info(
        action_message="充值套餐模块",
        detail=f"{uid}充值套餐模块{i}个，套餐模块{pack_id_list}, 操作人: {operator}",
    )

    return Success(msg=f"充值成功")


@api.route('/add', methods=['POST'])
@permission_required('admin')
def create_balance():
    allow_args = ('uid', )
    request_args = get_request_args(allow_args)

    uid = request_args.get('uid')
    if not uid or not UserHelper(request_args).get_data():
        raise ServerError(msg="用户不存在")

    now_datetime = arrow.now().datetime

    free_pack = Packages.objects.filter(key='scanv_free').first()
    balance = Balances(
        planId=free_pack.id,
        planChargeTime=now_datetime,
        planExpireTime=None,
        uid=uid
    )
    balance.balance.alertReceiver.used = 1
    balance.balance.alertGroup.used = 1
    balance.save()
    Charges(funcType='alertReceiver', limitType=LimitType.limit.value, used=1, chargeType='plan', uid=uid).save()
    Charges(funcType='alertGroup', limitType=LimitType.limit.value, used=1, chargeType='plan', uid=uid).save()

    CUserLog().info(action_message='创建balance', detail=f"uid: {uid}")

    return jsonify({'code': 200, 'msg': 'ok', 'results': {'balance_id': balance.id}})


@api.route('/cas', methods=['POST'])
def cas_api():
    data = request.json
    flag, resp = request_cas(data)
    if not flag:
        return jsonify({"status": "failed", "message": resp, "data": {}})
    else:
        return jsonify({"status": "success", "data": resp, "message": ""})


@api.route('/balance/get/<string:uid>', methods=['GET'])
def get_balance_by_uid(uid):
    try:
        uid = ObjectId(uid)
    except Exception:
        raise ParameterError(msg='uid参数错误')

    data = {
        'uid': str(uid),
        'package': dict(),
        'balance': dict(),
    }
    user = User.objects.find_one({"_id": uid})
    if user:
        packages_id = user.planId
        balances_id = user.balanceId

        if balances_id:
            balances = Balances.objects.find_one({"_id": balances_id})
            data['balance'] = balances

        if packages_id:
            packages = Packages.objects.find_one({"_id": packages_id})
            data['package'] = packages

    return jsonify({'code': 200, 'msg': 'ok', 'data': data})


@api.route('/wechat_qrcode', methods=['POST'])
def wechat_qrcode():
    data = {
        "url": "/accounts/scanv/get_qrcode_url/",
        "body": {"username": g.user.username}
    }
    flag, resp = request_cas(data)
    if not flag:
        return jsonify({"status": "failed", "msg": resp, "data": {}})
    else:
        return jsonify({"status": "success", "data": resp, "msg": ""})


@api.route('/wechat_unbind', methods=['POST'])
def wechat_bind():
    user = g.user
    data = {
        "url": "/accounts/scanv/wx_unbind/",
        "body": {"username": user.username}
    }
    flag, resp = request_cas(data)
    if not flag:
        return jsonify({"status": "failed", "msg": resp, "data": {}})
    else:
        user.update(wechat={})
        return jsonify({"status": "success", "data": resp, "msg": ""})


@api.route("/agreement", methods=["GET"])
def user_agreement():
    user = g.user
    data = {
        "uid": user.id,
        "disclaimer_agree_time": user.disclaimerAgreeTime or ""
    }
    return jsonify({"status": "success", "code": 200, "result": data, "msg": "ok"})


@api.route("/agreement", methods=["POST"])
def agreement():
    user = g.user
    form = AgreementForm().validate_for_api()
    enable = form.enable.data
    if user.disclaimerAgreeTime:
        return jsonify({"status": "success", "code": 200, "msg": "您已经签署了《知道创宇ScanV服务条款》"})
    flag, msg = agree_user_protocol(user=user, allow_protocol=enable)
    if not flag:
        raise ParameterError(msg=msg)
    log_data = {
        "message": "签署《知道创宇ScanV服务条款》",
        "detail": "签署《知道创宇ScanV服务条款》",
    }
    UUserLog(**log_data).save()
    return jsonify({"status": "success", "code": 200, "msg": msg})
